package com.fanhang.samples.spring.jdbc.common;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;
import javax.sql.DataSource;

import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.SingleColumnRowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.util.Assert;

/**
 * 数据库操作的封装
 */
public class BaseJdbcDaoSupport<T extends Serializable> extends NamedParameterJdbcDaoSupport {
	
	private Class<T> entityClass;
	
	@Resource(name = "dataSource")
	private void init(DataSource dataSource) {
		super.setDataSource(dataSource);
	}
	
	private final ResultSetExtractor<List<String[]>> RSE_STRING_ARRAY = new ResultSetExtractor<List<String[]>>(){
		@Override
		public List<String[]> extractData(ResultSet rs) throws DataAccessException, DataAccessException, SQLException {
			ResultSetMetaData rsmd = rs.getMetaData();
			int columnCount = rsmd.getColumnCount();
			
			List<String[]> result = new ArrayList<String[]>();
			String[] row = null;
			while (rs.next()) {
				row = new String[columnCount];
				for (int i = 0; i < columnCount; i++) {
					row[i] = rs.getString(i+1);
				}
				result.add(row);
			}
			return result;
		}
	};
	
	@SuppressWarnings("unchecked")
	public BaseJdbcDaoSupport() {
		Type t = getClass().getGenericSuperclass();
		System.out.println(t);
		Type[] types = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments();
		if (types != null && types.length > 0) {
			entityClass = (Class<T>)types[0];
		}
	}
	

	protected int update(CharSequence sql) {
		Assert.notNull(sql, "[sql] must not be null");
		return super.getJdbcTemplate().update(sql.toString());
	}
	
	/**
	 * 
	 * @param sql
	 * @param bean
	 * @return
	 * @see NamedParameterJdbcDaoSupport
	 */
	protected int update(CharSequence sql, Object bean) {
		Assert.notNull(sql, "[sql] must not be null");
		Assert.notNull(bean, "[bean] must not be null");
		SqlParameterSource param = new BeanPropertySqlParameterSource(bean);
		return super.getNamedParameterJdbcTemplate().update(sql.toString(), param);
	}
	protected int update(String sql, Object[] args) throws DataAccessException {
		Assert.notNull(sql, "[sql] must not be null");
		Assert.notNull(args, "[args] must not be null");
		return super.getJdbcTemplate().update(sql.toString(), args);
	}
	protected int[] update(String[] sqls) throws DataAccessException {
		int[] updatedRows = new int[sqls.length];
		for (int i = 0; i < sqls.length; i++) {
			updatedRows[i] = update(sqls[i]);
		}
		return updatedRows;
	}

	protected int updates(String sql, Object[] params) throws DataAccessException, SQLException {
		Connection conn = super.getConnection();
		PreparedStatement stmt = null;
		try {
			stmt = conn.prepareStatement(sql);
			int rows = 0;
			if (params != null) {
				for (int i = 0; i < params.length; i++) {
					stmt.setObject(1, params[i]);
					rows += stmt.executeUpdate();
				}
			}
			return rows;
		} finally {
			//TODO updates
		}
	}
	protected int[] updates(String sql, List<Object[]> argsList) throws DataAccessException {
		return super.getJdbcTemplate().batchUpdate(sql, argsList);
	}
	protected int[] update(String[] sqls, Object[] params) throws DataAccessException {
		//TODO updates
		int[] updatedRows = new int[sqls.length];
		for (int i = 0; i < sqls.length; i++) {
			updatedRows[i] = update(sqls[i], params);
		}
		return updatedRows;
	}
	protected int[] updates(String[] sqls, Object[] params) throws DataAccessException, SQLException {
		//TODO updates
		int[] updatedRows = new int[sqls.length];
		for (int i = 0; i < sqls.length; i++) {
			updatedRows[i] = updates(sqls[i], params);
		}
		return updatedRows;
	}
	protected int[] update(String[] sqls, Object[][] params) throws DataAccessException {
		//TODO updates
		int[] updatedRows = new int[sqls.length];
		for (int i = 0; i < sqls.length; i++) {
			updatedRows[i] = update(sqls[i], params[i]);
		}
		return updatedRows;
	}
	protected int[] updates(String[] sqls, Object[][] params) throws DataAccessException, SQLException {
		//TODO updates
		int[] updatedRows = new int[sqls.length];
		for (int i = 0; i < sqls.length; i++) {
			updatedRows[i] = updates(sqls[i], params[i]);
		}
		return updatedRows;
	}
	
	/**
	 * 根据SQL查询数据库，获得一个查询条数
	 * 
	 * @param sql
	 *            SQL查询语句
	 * @return List(String)
	 * @throws DataAccessException
	 */
	protected int getCount(String sql) throws DataAccessException {
		return super.getJdbcTemplate().queryForObject(sql, Integer.class);
	}
	
	/**
	 * 根据SQL查询数据库，获得一个查询条数
	 * 
	 * @param sql
	 *            SQL查询语句
	 * @return List(String)
	 * @throws DataAccessException
	 */
	protected int getCount(String sql, Object[] args) throws DataAccessException {
		return super.getJdbcTemplate().queryForObject(sql, Integer.class, args);
	}
	
	/**
	 * 根据SQL查询查询数据条数
	 * 
	 * @throws DataAccessException
	 * @see NamedParameterJdbcDaoSupport
	 */
	protected int getCount(String sql, Object bean) throws DataAccessException {
		SqlParameterSource param = new BeanPropertySqlParameterSource(bean);
		return super.getNamedParameterJdbcTemplate().queryForObject(sql, param, Integer.class);
	}
	
	/**
	 * 根据SQL查询数据库，获得一个String元素的列表。
	 * 
	 * @param sql
	 *            SQL查询语句
	 * @return List(String)
	 * @throws DataAccessException
	 */
	protected List<String> getStringList(String sql) throws DataAccessException {
		return super.getJdbcTemplate().query(sql, new SingleColumnRowMapper<String>());
	}

	/**
	 * 利用预编译SQL语句查询数据库，获得元素为String的列表。
	 * 
	 * @param sql
	 *            预编译SQL语句
	 * @param params
	 *            预编译SQL参数，可为null。
	 * @return List(String)元素为String的列表
	 * @throws DataAccessException
	 */
	protected List<String> getStringList(String sql, Object[] params) throws DataAccessException {
		return super.getJdbcTemplate().query(sql, params, new SingleColumnRowMapper<String>());
	}

	/**
	 * 根据SQL语句查询数据库，并从结果集的起始列至终止列获得元素为String[]的列表。
	 * 
	 * @param sql
	 *            SQL查询语句
	 * @return List(String[])元素为String[]的列表
	 * @throws DataAccessException
	 */
	protected T get(String sql) throws DataAccessException {
		return this.get(sql, new Object[]{});
	}
	
	/**
	 * 根据SQL语句查询数据库，并从结果集的起始列至终止列获得元素为String[]的列表。
	 * 
	 * @param sql
	 *            SQL查询语句
	 * @return List(String[])元素为String[]的列表
	 * @throws DataAccessException
	 */
	protected T get(String sql, Object[] params) throws DataAccessException {
		return this.get(sql, params, entityClass);
	}
	
	/**
	 * 根据SQL语句查询数据库，并从结果集的起始列至终止列获得元素为String[]的列表。
	 * 
	 * @param sql
	 *            SQL查询语句
	 * @return List(String[])元素为String[]的列表
	 * @throws DataAccessException
	 */
	protected T get(String sql, Object[] params, Class<T> clazz) throws DataAccessException {
		Assert.hasLength(sql);
		Assert.notNull(clazz);
		return super.getJdbcTemplate().queryForObject(sql, params, new BeanPropertyRowMapper<T>(clazz));
//		try {
//			
//		} catch (EmptyResultDataAccessException e) {
//			return null;
//		}
	}
	
	/**
	 * 根据SQL语句查询数据库，并从结果集的起始列至终止列获得元素为String[]的列表。
	 * 
	 * @param sql
	 *            SQL查询语句
	 * @return List(String[])元素为String[]的列表
	 * @throws DataAccessException
	 */
	protected List<String[]> getList(String sql) throws DataAccessException {
		return super.getJdbcTemplate().query(sql, RSE_STRING_ARRAY);
	}

	/**
	 * @throws DataAccessException
	 */
	protected List<String[]> getList(String sql, Object[] params) throws DataAccessException {
		return super.getJdbcTemplate().query(sql, params, RSE_STRING_ARRAY);
	}

	/**
	 * @throws DataAccessException
	 */
	protected List<T> getList(String sql, Object model, Class<T> clazz) throws DataAccessException {
		BeanPropertySqlParameterSource param = new BeanPropertySqlParameterSource(model);
		return super.getNamedParameterJdbcTemplate().query(sql, param, new BeanPropertyRowMapper<T>(clazz));
	}
	
	/**
	 * 根据分页信息对象查询出规定的一页数据列表。
	 * 
	 * @param sql
	 *            SQL语句
	 * @param startCol
	 *            获取结果集的起始列，从0开始（包含）
	 * @param endCol
	 *            获取结果集的终止列（不包）
	 * @param pagination
	 *            分页信息
	 * @return List(String[])一页数据列表
	 * @throws DataAccessException
	 */
	protected List<String[]> getPageList(String sql, final int start, final int end) throws DataAccessException {
		return super.getJdbcTemplate().query(sql, new ResultSetExtractor<List<String[]>>(){
			@Override
			public List<String[]> extractData(ResultSet rs) throws DataAccessException, DataAccessException, SQLException {
				ResultSetMetaData rsmd = rs.getMetaData();
				int columnCount = rsmd.getColumnCount();
				rs.absolute(start);
				int size = 0;
				List<String[]> result = new ArrayList<String[]>();
				String[] row = null;
				while (rs.next()) {
					if (size >= end) {
						break;
					}
					row = new String[columnCount];
					for (int i = 0; i < columnCount; i++) {
						row[i] = rs.getString(i+1);
					}
					result.add(row);
					size++;
				}
//				pagination.setRowSize(rs.getRow());
				return result;
			}
		});
	}

	/**
	 * 根据分页信息和SQL语句查询数据库获得一页数据列表。
	 * 
	 * @param sql
	 *            预编译SQL语句
	 * @param params
	 *            SQL参数，可为null。
	 * @param pagination
	 *            分页信息
	 * @return List(String[])一页数据列表
	 * @throws DataAccessException
	 */
	protected List<String[]> getPageList(String sql, Object[] params, final int start, final int end)
			throws DataAccessException {
		return super.getJdbcTemplate().query(sql, params, new ResultSetExtractor<List<String[]>>(){
			@Override
			public List<String[]> extractData(ResultSet rs) throws DataAccessException, SQLException {
				ResultSetMetaData rsmd = rs.getMetaData();
				int columnCount = rsmd.getColumnCount();
				rs.absolute(start);
				int size = 0;
				List<String[]> result = new ArrayList<String[]>();
				String[] row = null;
				while (rs.next()) {
					if (size >= end) {
						break;
					}
					row = new String[columnCount];
					for (int i = 0; i < columnCount; i++) {
						row[i] = rs.getString(i+1);
					}
					result.add(row);
					size++;
				}
//				pagination.setRowSize(rs.getRow());
				return result;
			}
		});
	}

	/**
	 * 执行一条 INSERT SQL 语句获得其中自增字段的值。
	 * 
	 * @param sql
	 *            INSERT SQL 语句
	 * @return 插入的自增字段的值
	 * @throws DataAccessException
	 * @throws SQLException 
	 */
	protected long getGeneratedKey(String sql) throws DataAccessException, SQLException {
		Connection conn = super.getConnection();
		Statement stmt = null;
		ResultSet rs = null;

		try {
			stmt = conn.createStatement();
			stmt.executeUpdate(sql, new int[] { 1 });
			rs = stmt.getGeneratedKeys();
			rs.next();
			return rs.getLong(1);
		} finally {
//			closeResultSet(rs);
//			closeStatement(stmt);
		}
	}

	/**
	 * 采用预编译SQL语句形式执行插入语句并获得自增键的值
	 * 
	 * @param sql
	 *            预编译 INSERT SQL 语句
	 * @param params
	 *            SQL参数，可为null。
	 * @return 插入的自增字段的值
	 * @throws DataAccessException
	 * @throws SQLException 
	 */
	protected long getGeneratedKey(String sql, Object[] params) throws DataAccessException, SQLException {
		Connection conn = super.getConnection();
		PreparedStatement stmt = null;
		ResultSet rs = null;

		try {
			stmt = conn.prepareStatement(sql, new int[] { 1 });
			if (params != null) {
				for (int i = 0; i < params.length; i++) {
					stmt.setObject(i + 1, params[i]);
				}
			}
			stmt.executeUpdate();
			rs = stmt.getGeneratedKeys();
			rs.next();
			return rs.getLong(1);
		} finally {
//			closeResultSet(rs);
//			closeStatement(stmt);
		}
	}

	/**
	 * 采用预编译SQL语句形式执行批量插入语句并获得自增键的值数组
	 * 
	 * @param sql
	 *            预编译 INSERT SQL 语句
	 * @param paramsList
	 *            SQL参数，可为null。
	 * @return 插入的自增字段的值
	 * @throws DataAccessException
	 * @throws SQLException 
	 */
	protected long[] getGeneratedKeys(String sql, List<String[]> paramsList) throws DataAccessException, SQLException {
		Connection conn = super.getConnection();
		PreparedStatement stmt = null;
		ResultSet rs = null;
		long[] ids = null;
		try {
			stmt = conn.prepareStatement(sql, new int[] { 1 });
			if (paramsList != null && paramsList.size() > 0) {
				ids = new long[paramsList.size()];
				for (int i = 0; i < paramsList.size(); i++) {
					String[] params = (String[]) paramsList.get(i);
					for (int j = 0; j < params.length; j++) {
						stmt.setObject(j + 1, params[j]);
					}
					stmt.executeUpdate();
					rs = stmt.getGeneratedKeys();
					rs.next();
					ids[i] = rs.getLong(1);
				}
			}
		} finally {
//			closeResultSet(rs);
//			closeStatement(stmt);
		}
		return ids;
	}

	

	

	/**
	 * 是否查询到数据
	 * 
	 * @param sql
	 *            查询SQL语句
	 * @return boolean 查询的结果集中有数据则返回true，否则返回false。
	 * @throws DataAccessException
	 */
	protected boolean hasData(String sql) throws DataAccessException {
		return super.getJdbcTemplate().query(sql, new ResultSetExtractor<Boolean>(){
			@Override
			public Boolean extractData(ResultSet rs) throws DataAccessException, SQLException {
				return rs.next();
			}
		});
	}

	/**
	 * 是否查询到数据
	 * 
	 * @param sqls
	 *            查询SQL语句组
	 * @return boolean 查询的结果集中有数据则返回true，否则返回false。
	 * @throws DataAccessException
	 */
	protected boolean hasData(String[] sqls) throws DataAccessException {
		for (int i = 0; i < sqls.length; i++) {
			if (hasData(sqls[i])) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 采用预编译SQL语句形式获得是否查询到数据
	 * 
	 * @param sql
	 *            预编译查询SQL语句
	 * @param params
	 *            SQL参数，可为null。
	 * @return boolean 查询的结果集中有数据则返回true，否则返回false。
	 * @throws DataAccessException
	 */
	protected boolean hasData(String sql, Object[] params) throws DataAccessException {
		return super.getJdbcTemplate().query(sql, params, new ResultSetExtractor<Boolean>(){
			@Override
			public Boolean extractData(ResultSet rs) throws DataAccessException, SQLException {
				return rs.next();
			}
		});
	}

	/**
	 * 采用预编译SQL语句形式获得是否查询到数据
	 * 
	 * @param sqls
	 *            预编译查询SQL语句组
	 * @param params
	 *            SQL参数，可为null。
	 * @return boolean 查询的结果集中有数据则返回true，否则返回false。
	 * @throws DataAccessException
	 */
	protected boolean hasData(String[] sqls, Object[] params) throws DataAccessException {
		for (int i = 0; i < sqls.length; i++) {
			if (hasData(sqls[i], params)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 采用预编译SQL语句形式获得是否查询到数据，该方法将执行<code>params.length</code>次SQL语句。
	 * 
	 * @param sql
	 *            预编译SQL语句，该SQL中只允许一个参数
	 * @param params
	 *            SQL参数，可为null。
	 * @return 查询到数据则返回true
	 * @throws DataAccessException
	 * @throws SQLException 
	 */
	protected boolean hasDatas(final String sql, final Object[] params) throws DataAccessException, SQLException {
		//TODO hasDatas
		super.getJdbcTemplate().query(new PreparedStatementCreator() {
			@Override
			public PreparedStatement createPreparedStatement(Connection con) throws DataAccessException, SQLException {
				con.prepareStatement(sql);
				
				return null;
			}
		}, new ResultSetExtractor<Boolean>(){
			@Override
			public Boolean extractData(ResultSet rs) throws DataAccessException, SQLException {
				return rs.next();
			}
		});
		
		Connection conn = super.getConnection();
		PreparedStatement stmt = null;
		ResultSet rs = null;
		try {
			stmt = conn.prepareStatement(sql);
			if (params != null) {
				for (int i = 0; i < params.length; i++) {
					stmt.setObject(1, params[i]);
					rs = stmt.executeQuery();
					if (rs.next()) {
						return true;
					}
				}
			}
			return false;
		} finally {
//			closeResultSet(rs);
//			closeStatement(stmt);
		}
	}

	/**
	 * 采用预编译SQL语句形式获得是否查询到数据，该方法将执行<code>params.length</code>次SQL语句。
	 * 
	 * @param sqls
	 *            预编译SQL语句组，该SQL中只允许一个参数
	 * @param params
	 *            SQL参数，可为null。
	 * @return 查询到数据则返回true
	 * @throws DataAccessException
	 * @throws SQLException 
	 */
	protected boolean hasDatas(String[] sqls, Object[] params) throws DataAccessException, SQLException {
		for (int i = 0; i < sqls.length; i++) {
			if (hasDatas(sqls[i], params)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 采用预编译SQL语句形式获得是否查询到数据，该方法将执行<code>paramList.size()</code>次SQL语句。
	 * 
	 * @param sql
	 *            预编译SQL语句，该SQL中只允许一个参数
	 * @param paramList
	 *            List(Object[]) SQL参数，列表中每一个数组元素为一组参数，可为null。
	 * @return 查询到数据则返回true
	 * @throws DataAccessException
	 * @throws SQLException 
	 */
	protected boolean hasDatas(String sql, List<String[]> paramList) throws DataAccessException, SQLException {
		Connection conn = super.getConnection();
		PreparedStatement stmt = null;
		ResultSet rs = null;
		try {
			stmt = conn.prepareStatement(sql);
			if (paramList != null) {
				for (int i = 0, ii = paramList.size(); i < ii; i++) {
					Object[] param = (Object[]) paramList.get(i);
					for (int j = 0; j < param.length; j++) {
						stmt.setObject(j + 1, param[j]);
					}
					rs = stmt.executeQuery();
					if (rs.next()) {
						return true;
					}
				}
			}
			return false;
		} finally {
//			closeResultSet(rs);
//			closeStatement(stmt);
		}
	}

	/**
	 * 采用预编译SQL语句形式获得是否查询到数据，该方法将执行<code>paramList.size()</code>次SQL语句。
	 * 
	 * @param sqls
	 *            预编译SQL语句，该SQL中只允许一个参数
	 * @param paramList
	 *            List(Object[]) SQL参数，列表中每一个数组元素为一组参数，可为null。
	 * @return 查询到数据则返回true
	 * @throws DataAccessException
	 * @throws SQLException 
	 */
	protected boolean hasDatas(String[] sqls, List<String[]> paramList) throws DataAccessException, SQLException {
		for (int i = 0; i < sqls.length; i++) {
			if (hasDatas(sqls[i], paramList)) {
				return true;
			}
		}
		return false;
	}

	protected int getInt(String sql) throws DataAccessException {
		return super.getJdbcTemplate().queryForObject(sql, Integer.class);
	}
	protected int getInt(String sql, Object[] params) throws DataAccessException {
		return super.getJdbcTemplate().queryForObject(sql, params, Integer.class);
	}

	protected long getLong(String sql) throws DataAccessException {
		return super.getJdbcTemplate().queryForObject(sql, Long.class);
	}
	protected long getLong(String sql, Object[] params) throws DataAccessException {
		return super.getJdbcTemplate().queryForObject(sql, params, Long.class);
	}

	protected float getFloat(String sql) throws DataAccessException {
		return super.getJdbcTemplate().queryForObject(sql, Float.class);
	}
	protected float getFloat(String sql, Object[] params) throws DataAccessException {
		return super.getJdbcTemplate().queryForObject(sql, params, Float.class);
	}

	protected double getDouble(String sql) throws DataAccessException {
		return super.getJdbcTemplate().queryForObject(sql, Double.class);
	}
	protected double getDouble(String sql, Object[] params) throws DataAccessException {
		return super.getJdbcTemplate().queryForObject(sql, params, Double.class);
	}

	protected String getString(String sql) throws DataAccessException {
		return super.getJdbcTemplate().queryForObject(sql, String.class);
	}
	protected String getString(String sql, Object[] params) throws DataAccessException {
		return super.getJdbcTemplate().queryForObject(sql, params, String.class);
	}
}
